<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>

    <!-- 引入方式1 内联方式 可以放在<html></html>的任何位置-->
    <!--    <script type="text/javascript" > -->
    <!--        alert('Hello,world'); -->
    <!--    </script> -->

    <!-- 引入方式2 外联方式 可以放在<html></html>的任何位置 -->
    <!-- <script src="./js/js01.js" type="text/javascript"></script> -->


</head>
<body>
<script type="text/javascript">

    'use strict' //用于启用严格模式
    Infinity;
    // Infinity表示无限大，当数值超过了JavaScript的Number所能表示的最大值时，就表示为Infinity
    //JavaScript的整数最大范围不是±2^63，而是±2^53，因此，超过2^53的整数就可能无法精确表示：

    // 打印Number能表示的最大整数:
    console.log(Number.MAX_SAFE_INTEGER); // 9007199254740991

    //要精确表示比2^53还大的整数，可以使用内置的BigInt类型，它的表示方法是在整数后加一个n
    //也可以使用BigInt()把Number和字符串转换成BigInt
    let bi1 = 9223372036854775807n;
    let bi2 = BigInt(12345);
    let bi3 = BigInt("0x7fffffffffffffff");
    console.log(bi1 === bi2); //false
    console.log(bi1 === bi3); //true
    console.log(bi1 + bi2); //true

    // 使用BigInt可以正常进行加减乘除等运算，结果仍然是一个BigInt，但不能把一个BigInt和一个Number放在一起运算
    console.log(1234567n + 3456789n); // OK
    console.log(1234567n / 789n); // 1564, 除法运算结果仍然是BigInt
    console.log(1234567n % 789n); // 571, 求余
    // console.log(1234567n + 3456789); // Uncaught TypeError: Cannot mix BigInt and other types

    false == 0; // true
    false === 0; // false

    //第一种是==比较，它会自动转换数据类型再比较，很多时候，会得到非常诡异的结果；
    //第二种是===比较，它不会自动转换数据类型，如果数据类型不一致，返回false，如果一致，再比较。即机会

    //由于JavaScript这个设计缺陷，不要使用==比较，始终坚持使用===比较。

    //NaN这个特殊的Number与所有其他值都不相等，包括它自己
    NaN === NaN; //false

    //唯一能判断NaN的方法是通过isNaN()函数：
    isNaN(NaN); //true

    //最后要注意浮点数的相等比较：
    1 / 3 === (1 - 2 / 3); //false 因为精度问题
    //正确比较方式
    Math.abs(1/3 - (1 - 2/3)) < 0.0000001; //true

    //JavaScript中用null表示一个空的值，而undefined表示值未定义
    //大多数情况下，我们都应该用null。undefined仅仅在判断函数参数是否传递的情况下有用。

    //javaScript中的数组
    let arr = [1, 2, 3.14, 'Hello', null, true]; //数组是一组按顺序排列的集合，集合的每个值称为元素，考虑可读性问题，建议直接使用[]
    // let arr1 = new Array(1, 2, 3.14, 'Hello', null);
    alert(arr[2]);
    alert(arr[6]); // 索引超出了范围，返回undefined

    //要取得Array的长度，直接访问length属性
    arr.length;
    //请注意，直接给Array的length赋一个新的值会导致Array大小的变化,若length变大，则扩充的位置都是undefined,若length变小，则会截断
    //修改数组中的元素值 如：
    arr[1] = 99;

    //请注意，如果通过索引赋值时，索引超过了范围，同样会引起Array大小的变化,超出部分未赋值的仍然是undefined

    //slice() 范围同样是左闭右开
    //slice()就是对应String的substring()版本，它截取Array的部分元素，然后返回一个新的Array
    arr.slice(0, 3); // 从索引0开始，到索引3结束，但不包括索引3: [1, 2, 3.14]
    arr.slice(3); // 从索引3开始到结束: ['Hello', null, true]
    arr.slice();//截取数组全部元素,也就相当于copy数组

    //push和pop
    //push()向Array的末尾添加若干元素，pop()则把Array的最后一个元素删除掉
    arr.pop(); // 空数组继续pop不会报错，而是返回undefined

    //unshift()和shift()
    //如果要往Array的头部添加若干元素，使用unshift()方法，shift()方法则把Array的第一个元素删掉
    arr.shift(); // 空数组继续shift不会报错，而是返回undefined

    //sort()
    let arr1 = ['B','C','A'];
    arr1.sort();
    arr1; //['A','B','C']

    //reverse()
    arr1.reverse();

    //splice() 修改Array的“万能方法”，它可以从指定的索引开始删除若干元素，然后再从该位置添加若干元素
    //splice(起始索引，元素个数，.....在删除位置新增元素列表)
    // 从索引2开始删除3个元素,然后再添加两个元素:
    let arr2 = ['Microsoft', 'Apple', 'Yahoo', 'AOL', 'Excite', 'Oracle'];
    arr2.splice(2, 3, 'Google', 'Facebook'); // 返回删除的元素 ['Yahoo', 'AOL', 'Excite']
    arr2; //['Microsoft', 'Apple', 'Google', 'Facebook', 'Oracle']
    // 只删除,不添加:
    arr.splice(2, 2); // ['Google', 'Facebook']
    // 只添加,不删除:
    arr.splice(2, 0, 'Google', 'Facebook'); // 返回[],因为没有删除任何元素

    //concat()
    //concat()方法把当前的Array和另一个Array连接起来，并返回一个新的Array
    let arr3 = ['A', 'B', 'C'];
    let added = arr3.concat([1, 2, 3]); //['A', 'B', 'C',1,2,3]

    //join() 方法是一个非常实用的方法，它把当前Array的每个元素都用指定的字符串连接起来，然后返回连接后的字符串
    added.join('-');

    //练习：在新生欢迎会上，你已经拿到了新同学的名单，请排序后显示：欢迎XXX，XXX，XXX和XXX同学
    let arr4 = ['小明', '小红', '大军', '阿黄'];
    arr4.sort();
    console.log(`欢迎${arr4[0]}，${arr4[1]}，${arr4[2]}和${arr4[3]}同学！`); //使用了模版字符串
    //欢迎大军，小明，小红和阿黄同学！3

    //javaScript中的对象 --- JavaScript的对象是一组由键-值组成的无序集合
    var person = {
        name:'Bob',
        age:20,
        tags:['js','web','mobile'],
        city:'BeiJing',
        hasCar:true,
        'middle-school':'No.1 Middle School', //属性名包含特殊字符（-）,不是一个有效的变量,就需要用个单引号''括起来
        zipcode:null //对象的最后一个属性不加逗号
    };
    
    //获取一个对象的属性
    alert(person.name); //Bob
    alert(person['name']); //Bob
    console.log(person['middle-school']); //// 'No.1 Middle School'

    //由于JavaScript的对象是动态类型，你可以自由地给一个对象添加或删除属性：
    person.sex = '男'; //添加
    delete person.sex; //删除属性，删除不存在的属性也不会报错

    //如果我们要检测xiaoming是否拥有某一属性，可以用in操作符：
    console.log('name' in person); //true
    console.log('grade' in person); //false
    /*
    不过要小心，如果in判断一个属性存在，这个属性不一定是person的，它可能是person继承得到的
    因为toString定义在object对象中，而所有对象最终都会在原型链上指向object，所以person也拥有toString属性。
    要判断一个属性是否是person自身拥有的，而不是继承得到的，可以用hasOwnProperty()方法：
    */
    console.log(person.hasOwnProperty('name')); //true
    console.log(person.hasOwnProperty('toString')); //false

    //js中的赋值运算符
    let x = 10;
    x = x + 2;
    alert(x);

    /*
    function hello() {
        'use strict';
        helloStr = 'hello'; //ReferenceError
        console.log(helloStr);
    }
    hello();
    */

    //转义字符
    //ASCII字符可以以\x##形式的十六进制表示
    alert('\x41'); //A
    //还可以用\u####表示一个Unicode字符：
    alert('\u4e2d\u56FD'); //中国

    //多行字符串
    var body = document.getElementsByTagName('body'); //返回的是一个类数组对象，不是单个的DOM元素
    body[0].innerText = `这是一个
                    多行
                    字符串`;

    // var body = document.getElementsByTagName('body')[0]; //返回的是一个类数组对象，不是单个的DOM元素
    // body.innerText = `这是第二个
    //                 多行
    //                 字符串，换了一种殊途同归的方法`;
</script>
<script src="./js/js01.js" type="text/javascript"></script>
</body>
</html>